using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Reflector.CodeModel;
using System.ComponentModel;

namespace Doubler.Helpers.Graph
{
    public sealed class CallGraph
    {
        
#region Fields

        private IMethodDeclaration _Method;
        private Dictionary<IArgumentReferenceExpression, CallsFromArgument> _FromArguments;
        private Dictionary<IPropertyReferenceExpression, CallsFromProperty> _FromProperties;
        private Dictionary<IFieldReferenceExpression, CallsFromField> _FromFields;
        private Dictionary<IVariableReferenceExpression, CallsFromVariable> _FromVariables;
        private Dictionary<IThisReferenceExpression, CallsFromThis> _FromThis;
        private Dictionary<ITypeReferenceExpression, CallsFromStatic> _FromStatics;
        private List<IPropertyReferenceExpression> _PropertiesAssigned;
        
#endregion

#region Properties

        public List<IPropertyReferenceExpression> PropertiesAssigned
        {
            get
            {
                if (_PropertiesAssigned == null)
                    _PropertiesAssigned = new List<IPropertyReferenceExpression>();
                return _PropertiesAssigned;
            }
            set
            {
                _PropertiesAssigned = value;
            }
        }

        public IMethodDeclaration Method
        {
            get
            {
                return _Method;
            }
            set
            {
                _Method = value;
            }
        }

        public Dictionary<IExpression, CallsFrom> AllCalls
        {
            get
            {
                Dictionary<IExpression, CallsFrom> _AllCalls = new Dictionary<IExpression, CallsFrom>();
                
                foreach (KeyValuePair<IArgumentReferenceExpression, CallsFromArgument> Pair in this.FromArguments)
                {
                    _AllCalls.Add(Pair.Key, Pair.Value);
                }
                foreach (KeyValuePair<IFieldReferenceExpression, CallsFromField> Pair in this.FromFields)
                {
                    _AllCalls.Add(Pair.Key, Pair.Value);
                }
                foreach (KeyValuePair<IPropertyReferenceExpression, CallsFromProperty> Pair in this.FromProperties)
                {
                    _AllCalls.Add(Pair.Key, Pair.Value);
                }
                foreach (KeyValuePair<ITypeReferenceExpression, CallsFromStatic> Pair in this.FromStatics)
                {
                    _AllCalls.Add(Pair.Key, Pair.Value);
                }
                foreach (KeyValuePair<IThisReferenceExpression, CallsFromThis> Pair in this.FromThis)
                {
                    _AllCalls.Add(Pair.Key, Pair.Value);
                }
                foreach (KeyValuePair<IVariableReferenceExpression, CallsFromVariable> Pair in this.FromVariables)
                {
                    _AllCalls.Add(Pair.Key, Pair.Value);
                }

                return _AllCalls;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public Dictionary<IThisReferenceExpression, CallsFromThis> FromThis
        {
            get
            {
                if (_FromThis == null)
                    _FromThis = new Dictionary<IThisReferenceExpression, CallsFromThis>();
                return _FromThis;
            }
            set
            {
                _FromThis = value;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public Dictionary<IArgumentReferenceExpression, CallsFromArgument> FromArguments
        {
            get
            {
                if (_FromArguments == null)
                    _FromArguments = new Dictionary<IArgumentReferenceExpression, CallsFromArgument>();
                return _FromArguments;
            }
            set
            {
                _FromArguments = value;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public Dictionary<IPropertyReferenceExpression, CallsFromProperty> FromProperties
        {
            get
            {
                if (_FromProperties == null)
                    _FromProperties = new Dictionary<IPropertyReferenceExpression, CallsFromProperty>();
                return _FromProperties;
            }
            set
            {
                _FromProperties = value;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public Dictionary<IFieldReferenceExpression, CallsFromField> FromFields
        {
            get
            {
                if (_FromFields == null)
                    _FromFields = new Dictionary<IFieldReferenceExpression, CallsFromField>();
                return _FromFields;
            }
            set
            {
                _FromFields = value;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public Dictionary<IVariableReferenceExpression, CallsFromVariable> FromVariables
        {
            get
            {
                if (_FromVariables == null)
                    _FromVariables = new Dictionary<IVariableReferenceExpression, CallsFromVariable>();
                return _FromVariables;
            }
            set
            {
                _FromVariables = value;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public Dictionary<ITypeReferenceExpression, CallsFromStatic> FromStatics
        {
            get
            {
                if (_FromStatics == null)
                    _FromStatics = new Dictionary<ITypeReferenceExpression, CallsFromStatic>();
                return _FromStatics;
            }
            set
            {
                _FromStatics = value;
            }
        }
        
#endregion
        
#region Constructors

        public CallGraph(IMethodDeclaration method)
        {
            _Method = method;
        }

#endregion

    }

    public class CallChain
    {
        
        private CallChain _Chain;
        private IExpression _Call;

        public virtual IExpression Call
        {
            get
            {
                return _Call;
            }
            set
            {
                _Call = value;
            }
        }

        public CallChain Chain
        {
            get
            {
                return _Chain;
            }
            set
            {
                _Chain = value;
            }
        }

        public CallChain LastCall
        {
            get
            {
                if (this.Chain != null)
                    return this.Chain.LastCall;
                return this;
            }
        }

        public CallChain(IExpression call, CallChain chain)
        {
            _Call = call;
            _Chain = chain;
        }

        public CallChain(IExpression call)
        {
            this.Call = call;
        }
    }

    public class MethodCallChain : CallChain
    {

        public MethodCallChain(IMethodReferenceExpression call, CallChain chain)
            : base(call, chain)
        {
        }

        public MethodCallChain(IMethodReferenceExpression call)
            : base(call)
        {
        }

        public new IMethodReferenceExpression Call
        {
            get
            {
                return (IMethodReferenceExpression)base.Call;
            }
            set
            {
                base.Call = value;
            }
        }
    }

    public class PropertyCallChain : CallChain
    {

        public PropertyCallChain(IPropertyReferenceExpression call, CallChain chain)
            : base(call, chain)
        {
        }

        public PropertyCallChain(IPropertyReferenceExpression call)
            : base(call)
        {
        }

        public new IPropertyReferenceExpression Call
        {
            get
            {
                return (IPropertyReferenceExpression)base.Call;
            }
            set
            {
                base.Call = value;
            }
        }
    }

    public class FieldCallChain : CallChain
    {

        public FieldCallChain(IFieldReferenceExpression call, CallChain chain)
            : base(call, chain)
        {
        }

        public FieldCallChain(IFieldReferenceExpression call)
            : base(call)
        {
        }

        public new IFieldReferenceExpression Call
        {
            get
            {
                return (IFieldReferenceExpression)base.Call;
            }
            set
            {
                base.Call = value;
            }
        }
    }

    public class EventCallChain : CallChain
    {

        public EventCallChain(IEventReferenceExpression call, CallChain chain)
            : base(call, chain)
        {
        }

        public EventCallChain(IEventReferenceExpression call)
            : base(call)
        {
        }

        public new IEventReferenceExpression Call
        {
            get
            {
                return (IEventReferenceExpression)base.Call;
            }
            set
            {
                base.Call = value;
            }
        }
    }

    public class CallsFrom
    {
        private List<CallChain> _CallChains;
        private IExpression _ParentTarget;

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public List<CallChain> CallChains
        {
            get
            {
                if (_CallChains == null)
                    _CallChains = new List<CallChain>();
                return _CallChains;
            }
            set
            {
                _CallChains = value;
            }
        }

        public IExpression ParentTarget
        {
            get
            {
                return _ParentTarget;
            }
            set
            {
                _ParentTarget = value;
            }
        }

        public CallsFrom()
        {
            
        }

        public CallsFrom(IExpression parentTarget)
        {
            _ParentTarget = parentTarget;
        }

        public CallsFrom(List<CallChain> callChains, IExpression parentTarget)
        {
            _CallChains = callChains;
            _ParentTarget = parentTarget;
        }
    }

    public class CallsFromArgument : CallsFrom
    {
        public new IArgumentReferenceExpression ParentTarget
        {
            get
            {
                return (IArgumentReferenceExpression)base.ParentTarget;
            }
            set
            {
            	base.ParentTarget = value;
            }
        }

        public CallsFromArgument() : base()
        {
        }

        public CallsFromArgument(IArgumentReferenceExpression parentTarget)
            : base(parentTarget)
        {
        }

        public CallsFromArgument(List<CallChain> callChains, IExpression parentTarget) : base(callChains, parentTarget)
        {
        }
    }

    public class CallsFromVariable : CallsFrom
    {
        public new IVariableReferenceExpression ParentTarget
        {
            get
            {
                return (IVariableReferenceExpression)base.ParentTarget;
            }
            set
            {
                base.ParentTarget = value;
            }
        }

        public CallsFromVariable()
            : base()
        {
        }

        public CallsFromVariable(IVariableReferenceExpression parentTarget)
            : base(parentTarget)
        {
        }

        public CallsFromVariable(List<CallChain> callChains, IVariableReferenceExpression parentTarget)
            : base(callChains, parentTarget)
        {
        }
    }

    public class CallsFromProperty : CallsFrom
    {
        public new IPropertyReferenceExpression ParentTarget
        {
            get
            {
                return (IPropertyReferenceExpression)base.ParentTarget;
            }
            set
            {
                base.ParentTarget = value;
            }
        }

        public CallsFromProperty()
            : base()
        {
        }

        public CallsFromProperty(IPropertyReferenceExpression parentTarget)
            : base(parentTarget)
        {
        }

        public CallsFromProperty(List<CallChain> callChains, IPropertyReferenceExpression parentTarget)
            : base(callChains, parentTarget)
        {
        }
    }

    public class CallsFromField : CallsFrom
    {
        public new IFieldReferenceExpression ParentTarget
        {
            get
            {
                return (IFieldReferenceExpression)base.ParentTarget;
            }
            set
            {
                base.ParentTarget = value;
            }
        }

        public CallsFromField()
            : base()
        {
        }

        public CallsFromField(IFieldReferenceExpression parentTarget)
            : base(parentTarget)
        {
        }

        public CallsFromField(List<CallChain> callChains, IFieldReferenceExpression parentTarget)
            : base(callChains, parentTarget)
        {
        }
    }

    public class CallsFromThis : CallsFrom
    {
        public new IThisReferenceExpression ParentTarget
        {
            get
            {
                return (IThisReferenceExpression)base.ParentTarget;
            }
            set
            {
                base.ParentTarget = value;
            }
        }

        public CallsFromThis()
            : base()
        {
        }

        public CallsFromThis(IThisReferenceExpression parentTarget)
            : base(parentTarget)
        {
        }

        public CallsFromThis(List<CallChain> callChains, IThisReferenceExpression parentTarget)
            : base(callChains, parentTarget)
        {
        }
    }

    public class CallsFromStatic : CallsFrom
    {
        public new ITypeReferenceExpression ParentTarget
        {
            get
            {
                return (ITypeReferenceExpression)base.ParentTarget;
            }
            set
            {
                base.ParentTarget = value;
            }
        }

        public CallsFromStatic()
            : base()
        {
        }

        public CallsFromStatic(ITypeReferenceExpression parentTarget)
            : base(parentTarget)
        {
        }

        public CallsFromStatic(List<CallChain> callChains, ITypeReferenceExpression parentTarget)
            : base(callChains, parentTarget)
        {
        }
    }

}
